{
  This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
  the Free Software Foundation; version 2 of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
}

// Copyright (c) 2010 2011 2012 - J. Aldo G. de Freitas Junior

{$mode objfpc}
{$H+}

Program
	XMLRegistry;

Uses
	Classes,
	SysUtils,
	BaseException,
	Stacks,
	StringArray,
	StrUtils,
	ExprNodes,
	ExprLoader,
	Commands,
	Functions,
	XMLNodes,
	XMLTemplate,
	Nodes,
	SimpleIPC,
	EventLog;

Const
	ccConfigFile = 'config.xml';

Var
	gConfig        : TDataBaseNode;
	gStack         : TVirtualMachineStack;
	gCommandLoader : TCommandParser;
	gNodeFunctions : TNodeFunctions;
	gIPCServer     : TSimpleIPCServer;
	gIPCClient     : TSimpleIPCClient;
	gEventLog      : TEventLog;

	gLine          : TStringStream;
	gFrom          : String;
	gFocused       : String;
	gCommandLine   : String;

Procedure ExecuteCommand(Const aFrom, aString : String);
Begin
	// Debug  WriteLn('->', gStack.Focused.IndexedName);
	// Debug  WriteLn('::', aString);
	Try
		Try
			gStack.Enter(0);
			gStack.ResultStr := '';
			gStack.ShouldEdit := False;
			gStack.EditFile := '';
			gStack.Editor := '';
			gStack.ResultEditCommand := '';
			gCommandLoader := TCommandParser.Create(Nil);
			gCommandLoader.DoLoadCommand(aFrom, aString);
			gCommandLoader.PropagateStack(gStack);
			gCommandLoader.Evaluate;
		Finally
			gStack.Leave(0);
			FreeAndNil(gCommandLoader);
		End;
	Except
		On E: EInterpreterException Do
		Begin
			gEventLog.Error(E.AsText);
			gStack.OutLn(E.AsText);
			gStack.ShouldEdit := False;
			gStack.EditFile := '';
			gStack.Editor := '';
			gStack.ResultEditCommand := '';
		End;
		On E: Exception Do
		Begin
			gEventLog.Error(E.Message);
			gStack.OutLn(E.Message);
			gStack.ShouldEdit := False;
			gStack.EditFile := '';
			gStack.Editor := '';
			gStack.ResultEditCommand := '';
		End;
	End;
	// Debug  WriteLn('<-', gStack.Focused.IndexedName);
End;

Procedure LoadConfig;
Begin
	Try
		gConfig.LoadFromFile(ccConfigFile);
	Except
		On E: EInterpreterException Do
			gEventLog.Error(E.AsText);
		On E: Exception Do
			gEventLog.Error(E.Message);
	End;
End;

Procedure Startup;
Begin
	gConfig := TDataBaseNode.Create(Nil);
	gStack := TVirtualMachineStack.Create;
	gNodeFunctions := TNodeFunctions.Create;
	gStack.RegisterFunctor(gNodeFunctions);
	gNodeFunctions.Stack := gStack;
	gStack.Focused := gConfig;
	gStack.Running := True;
	gStack.ResultStr := '';
	gStack.ShouldEdit := False;
	gStack.EditFile := '';
	gStack.Editor := '';
	gStack.ResultEditCommand := '';
	gIPCServer := TSimpleIPCServer.Create(Nil);
	gIPCServer.ServerID := 'xmlregistry';
	gIPCServer.Global := True;
	gIPCServer.StartServer;
	gEventLog := TEventLog.Create(Nil);
	gEventLog.LogType := ltSystem;
	gEventLog.Active := True;
	gEventLog.Log('OpenTik XMLRegistry started.');
End;

Procedure CloseUp;
Begin
	FreeAndNil(gNodeFunctions);
	FreeAndNil(gConfig);
	FreeAndNil(gStack);
	FreeAndNil(gIPCServer);
	gEventLog.Log('OpenTik XMLRegistry stopped.');
	FreeAndNil(gEventLog);
End;

Procedure ReadCommand;
Begin
	Try
		Try
			gLine := TStringStream.Create(gIPCServer.StringMessage);
			gFrom := gLine.ReadAnsiString;
			gFocused := gLine.ReadAnsiString;
			gCommandLine := gLine.ReadAnsiString;
		Finally
			FreeAndNil(gLine);
		End;
	Except
		On E: Exception Do
		Begin
			gCommandLine := '';
			gEventLog.Error(E.Message);
		End;
	End;
End;

Procedure ValidFocus;
Begin
	If Assigned(gStack.Focused) And Assigned(gStack.Focused.Owner) Then
		While Not gStack.Focused.IsCapableOf('focusable') do
			gStack.Focused := gStack.Focused.Owner As TXMLNode
	Else
		gStack.Focused := gConfig;
End;

Procedure SendResult;
Begin
	Try
		Try
			gIPCClient := TSimpleIPCClient.Create(Nil);
			gIPCClient.ServerID := gFrom;
			gIPCClient.Connect;
			gLine := TStringStream.Create('');
			If gStack.ShouldEdit Then
			Begin
				gLine.WriteByte(01);
				gLine.WriteAnsiString(gStack.Focused.IndexedName);
				gLine.WriteAnsiString(gStack.Focused.NumericalName);
				gLine.WriteAnsiString(gStack.ResultStr);
				gLine.WriteAnsiString(gStack.EditFile);
				gLine.WriteAnsiString(gStack.Editor);
				gLine.WriteAnsiString(gStack.ResultEditCommand);
			End
			Else
			Begin
				gLine.WriteByte(00);
				gLine.WriteAnsiString(gStack.Focused.IndexedName);
				gLine.WriteAnsiString(gStack.Focused.NumericalName);
				gLine.WriteAnsiString(gStack.ResultStr);
			End;
			gIPCClient.SendStringMessage(gLine.DataString);
			gIPCClient.Disconnect;
		Finally
			FreeAndNil(gIPCClient);
			FreeAndNil(gLine);
		End;
	Except 
		On E: Exception Do
			gEventLog.Error(E.Message);
	End;			
End;

Begin
	Startup;
	LoadConfig;
	Try
		ExecuteCommand('Implicit', '/scripts.|Name="autoexec"| execute');
		gStack.ResultStr := '';
		Repeat
			If gIPCServer.PeekMessage(100, True) Then
			Begin
				gFrom := '';
				gFocused := '';
				gCommandLine := '';
				ReadCommand;
				If gCommandLine <> '' Then
				Begin
					gStack.ResultStr := '';
					ExecuteCommand('Implicit', '/$root$.' + gFocused);
					gStack.ResultStr := '';
					ExecuteCommand('Console', gCommandLine);
					ValidFocus;
					gFocused := gStack.Focused.NumericalName;
					SendResult;
				End;
			End
			Else
				Sleep(10);
		Until Not gStack.Running;
	Finally
		CloseUp;
	End;
End.